#include <math.h>
#include "GradientDescent.h"


GradientDescent::GradientDescent(SystemView* s)
{
	sys=s;
	sorter = new Sorter(sys->getMy_luns(), 0);
	ssdDevice = sys->getDevice("SSD");
	fcDevice = sys->getDevice("FC");
	sataDevice = sys->getDevice("SATA");
	timeCalc = new TimeSliceCalculations(sys);
}


GradientDescent::~GradientDescent(void)
{
	delete sorter;
	delete timeCalc;
}

vector<ExtentStatsAlgo*>* GradientDescent::Run(vector<ExtentStatsAlgo*>* sortedExtents, int timeSliceCount, double g, int ssdSpace, int fcSpace, int sataSpace, int time){
	ssd = new vector<ExtentStatsAlgo*>();
	fc = new vector<ExtentStatsAlgo*>();
	sata = new vector<ExtentStatsAlgo*>();
	map<LUNID, LunTotalStatistics*>::iterator lun_it;

	map<LUNID, double> minPriorities;
	double minDamage=999999999;

	//normalize
	double norm =0.0;
	for(int i=0; i<timeSliceCount; i++)
		norm+=pow(g, i);
	vector<ExtentStatsAlgo*>::iterator extentIter;
	for (extentIter=sortedExtents->begin(); extentIter!=sortedExtents->end(); extentIter++){
		(*extentIter)->setTotalRM((*extentIter)->getTotalRM()/norm);
	}

	for (int k=0; k<10; k++){
		// sorting new activity vector
		vector<ExtentStatsAlgo*>* temp;
		temp = sorter->sort(sortedExtents);
		delete sortedExtents;
		sortedExtents = temp;

		for(extentIter = sortedExtents->begin(); extentIter != sortedExtents->end(); extentIter++){	
			if(ssd->size() < ssdSpace && ssdDevice->checkUtilization(time,(*extentIter))){
				ssdDevice->encUtilizationNoSave(time, *extentIter);
				ssd->push_back(*extentIter);
			}
			else if((fc->size() < fcSpace) && fcDevice->checkUtilization(time,*extentIter)){
				fcDevice->encUtilizationNoSave(time, *extentIter);
				fc->push_back(*extentIter);
			}
			else {
				if (sata->size() > sataSpace)
					cout<< "GD: SATA space is over!!!!!" << endl;
				if (!sataDevice->checkUtilization(time,*extentIter) )
					cout<< "GD: SATA util not pass!!!" << endl;
				sataDevice->encUtilizationNoSave(time, *extentIter);
				sata->push_back(*extentIter);
			}
		}

		double ssdUtil[4], fcUtil[4], sataUtil[4];
		for (int i=0; i<4; i++){
			ssdUtil[i]=ssdDevice->calcUtilGradientDescent(i);
			fcUtil[i]=fcDevice->calcUtilGradientDescent(i);
			sataUtil[i]=sataDevice->calcUtilGradientDescent(i);
		}

		// calling function to calculate each lun's response time for this time slice
		timeCalc->responseTime(ssd, fc, sata, time, ssdDevice, fcDevice, sataDevice, ssdUtil, fcUtil, sataUtil, 1);
		double tempD=timeCalc->sum_damage(time);
		map<LUNID, double>::iterator prioIter;
		if (tempD<minDamage){
			minDamage=tempD;
			for(lun_it = sys->getMy_luns()->begin(); lun_it != sys->getMy_luns()->end(); lun_it++){
				prioIter=minPriorities.find((*lun_it).first);
				if (prioIter!=minPriorities.end()){
					(*prioIter).second=(*lun_it).second->getLun()->getPriority();
				}
				else{
					minPriorities.insert(pair<LUNID, double> ((*lun_it).first, (*lun_it).second->getLun()->getPriority()));
				}
			}
		}
		double response_time, time_diff;
		for(lun_it = sys->getMy_luns()->begin(); lun_it != sys->getMy_luns()->end(); lun_it++){
			Lun* lun = lun_it->second->getLun();
			if (lun_it->second->getLunTimeStatistics(time) != 0) {
				response_time = lun_it->second->getLunTimeStatistics(time)->getResponseTime();
				time_diff = response_time - lun->getDRT();
				if (response_time>0){
					if (time_diff > 0){
						lun->setPriority(lun->getPriority()*2.2);
					}
					else {
						lun->setPriority(lun->getPriority()/1.1);
					}
				}
			}
		}

		ssd->clear();
		fc->clear();
		sata->clear();
		ssdDevice->clearUtilData();
		fcDevice->clearUtilData();
		sataDevice->clearUtilData();
	}

	delete ssd;
	delete fc;
	delete sata;

	for(lun_it = sys->getMy_luns()->begin(); lun_it != sys->getMy_luns()->end(); lun_it++){
		(*lun_it).second->getLun()->setPriority(minPriorities.find((*lun_it).first)->second);
	}
	return sortedExtents;
}
